package com.wwdx.shiro;

import com.wwdx.model.upms.UpmsPermission;
import com.wwdx.model.upms.UpmsRole;
import com.wwdx.model.upms.UpmsUser;
import com.wwdx.service.upms.UpmsApiServiceI;
import com.wwdx.utils.ConstantHolder;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 数据库认证及权限查询
 * @author liguoliang
 */
public class ShiroAuthorizingRealm extends AuthorizingRealm {

	private UpmsApiServiceI upmsApiService;
	private RedisTemplate<String,Map<String,Object>> redisTemplate;

	public ShiroAuthorizingRealm(UpmsApiServiceI upmsApiService, ShiroHashedCredentialsMatcher shiroHashedCredentialsMatcher, RedisTemplate<String,Map<String,Object>> redisTemplate) {
		//关闭认证缓存
		super.setAuthenticationCachingEnabled(false);
		//关闭授权缓存
		super.setAuthorizationCachingEnabled(false);
        //设定Password校验的Hash算法与迭代次数.
        super.setCredentialsMatcher(shiroHashedCredentialsMatcher);
        //Realm名称
		super.setName(GlobalStatic.AUTHORIZATION_REALMNAME);
        this.upmsApiService = upmsApiService;
        this.redisTemplate = redisTemplate;
	}

	/**
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		// 因为非正常退出，即没有显式调用 SecurityUtils.getSubject().logout()
		// (可能是关闭浏览器，或超时)，但此时缓存依旧存在(principals)，所以会自己跑到授权方法里。
        Subject subject = SecurityUtils.getSubject();
        if (!subject.isAuthenticated()) {
			super.doClearCache(principalCollection);
            subject.logout();
			return null;
		}

		SessionUser shiroUser = (SessionUser) principalCollection.getPrimaryPrincipal();
		UpmsUser upmsUser = upmsApiService.selectUpmsUserByUsername(shiroUser.getAccount());
        if (upmsUser == null){
            return null;
        }
        Integer userId = upmsUser.getUserId();

        //当前用户所有角色
		List<UpmsRole> upmsRoles = upmsApiService.selectUpmsRoleByUpmsUserId(userId);
		Set<String> roles = new HashSet<>();
		for (UpmsRole upmsRole : upmsRoles) {
            String name = upmsRole.getName();
            if (StringUtils.isNotBlank(name)) {
				roles.add(name);
			}
		}

		// 当前用户所有权限
		List<UpmsPermission> upmsPermissions = upmsApiService.selectUpmsPermissionByUpmsUserId(userId);
		Set<String> permissions = new HashSet<>();
		for (UpmsPermission upmsPermission : upmsPermissions) {
            String permissionValue = upmsPermission.getPermission_value();
            if (StringUtils.isNotBlank(permissionValue)) {
				permissions.add(permissionValue);
			}
		}

		// 添加角色及权限信息
		SimpleAuthorizationInfo sazi = new SimpleAuthorizationInfo();
		sazi.addRoles(roles);
		sazi.addStringPermissions(permissions);
		return sazi;
	}

	/**
	 * 认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		// 调用业务方法
		String userName = upToken.getUsername();
		//账号为空
		if(StringUtils.isBlank(userName)){
			return null;
		}

        //处理密码错误缓存
		String key = String.format(ConstantHolder.USER_LOGIN,userName);
		ValueOperations<String, Map<String,Object>> valueOperations = redisTemplate.opsForValue();
		Map<String,Object> map = valueOperations.get(key);
        if (!CollectionUtils.isEmpty(map)){
            Integer errorcount = Integer.valueOf(map.get("errorcount").toString());
            //密码连续错误10次以上
            if(errorcount >= GlobalStatic.ERROR_LOGIN_COUNT){
				String errorMessage = "密码连续错误超过" + GlobalStatic.ERROR_LOGIN_COUNT + "次,账号被锁定,请" + GlobalStatic.ERROR_LOGIN_LOCK_MINUTE + "分钟之后再尝试登录!";
				Long endDateLong = Long.valueOf(map.get("time").toString());
				Long now = System.currentTimeMillis() / 1000;
				if(endDateLong == 0){
                    endDateLong = now + GlobalStatic.ERROR_LOGIN_LOCK_MINUTE * 60;
                    map.put("errorcount",errorcount);
                    map.put("time", endDateLong);
					valueOperations.set(key, map);
                    throw new LockedAccountException(errorMessage);
                    //过了失效时间
                }else if(now > endDateLong){
                    redisTemplate.delete(key);
                }else {
                    throw new LockedAccountException(errorMessage);
                }
            }
        }

        //查询用户信息
        UpmsUser upmsUser = upmsApiService.selectUpmsUserByUsername(userName);
		if (null != upmsUser) {
            return new SimpleAuthenticationInfo(new SessionUser(upmsUser),
                    upmsUser.getPassword(), ByteSource.Util.bytes(userName + upmsUser.getSalt()), getName());
        }
		//认证失败
		throw new UnknownAccountException("账户不存在!");
	}
}